/**
 * 单词接龙
 *
 * 字典 wordList 中从单词 beginWord 到 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> ... -> sk：
 * 每一对相邻的单词只差一个字母。
 *  对于 1 <= i <= k 时，每个 si 都在 wordList 中。注意， beginWord 不需要在 wordList 中。
 * sk == endWord
 * 给你两个单词 beginWord 和 endWord 和一个字典 wordList ，返回 从 beginWord 到 endWord 的 最短转换序列 中的 单词数目 。如果不存在这样的转换序列，返回 0 。
 *
 * 示例 1：
 * 输入：beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"]
 * 输出：5
 * 解释：一个最短转换序列是 "hit" -> "hot" -> "dot" -> "dog" -> "cog", 返回它的长度 5。
 *
 * 示例 2：
 * 输入：beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log"]
 * 输出：0
 * 解释：endWord "cog" 不在字典中，所以无法进行转换。
 *
 * 提示：
 * 1 <= beginWord.length <= 10
 * endWord.length == beginWord.length
 * 1 <= wordList.length <= 5000
 * wordList[i].length == beginWord.length
 * beginWord、endWord 和 wordList[i] 由小写英文字母组成
 * beginWord != endWord
 * wordList 中的所有字符串 互不相同
 */

import java.util.*;

/**
 * 这一题还是与上一题有点不一样的, 上面一题需要记录所有的最短记录, 而这一题我们只需要
 * 把最短的长度计算出来就可以了, 这里因为我们每次都会把已经选过的放入我们 vis 中,
 * 并且q存的是我们的最近一步string, 所以不用担心
 * 时间复杂度 : O(M^2 × N)
 * 空间复杂度 : O(M × N)
 * M 为单词长度
 */

public class Main {
    public int ladderLength(String beginWord, String endWord, List<String> wordList) {

        // 存之前用过的, 很关键, 这里建议举例子说明一下
        Set<String> vis = new HashSet<>();

        // 存可以用的单词
        Set<String> hash = new HashSet<>();

        // 加入列表
        for (String str : wordList) hash.add(str);

        // 特殊例子
        if (beginWord.equals(endWord)) return 1;
        if (!hash.contains(endWord)) return 0;

        // 队列, 记录上一次的单词
        Queue<String> q = new LinkedList<>();

        // 步数
        int step = 0;

        // 先初始化
        q.add(beginWord);
        vis.add(beginWord);

        // 循环
        while (!q.isEmpty()) {

            // 这里将每一种都遍历都使用一次, 找最短步数
            int sz = q.size();
            step++;

            while (sz-- != 0) {

                String t = q.poll();

                for (int i = 0; i < beginWord.length(); i++) {

                    // 转化成数组, 方便后面替换字母
                    char[] tmp = t.toCharArray();

                    // 每一种都试一遍
                    for (char ch = 'a'; ch <= 'z'; ch++) {
                        tmp[i] = ch;
                        String next = new String(tmp);

                        // 第一层判断
                        if (!vis.contains(next) && hash.contains(next)) {

                            // 判断是否满足结束条件
                            if (endWord.equals(next)) return step + 1;

                            q.add(next);
                            vis.add(next);
                        }
                    }
                }
            }
        }

        // 没找到就说明没有, 返回 0
        return 0;
    }
}